iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
Mobile Development

從零開始學習 iOS系列 第 20

從零開始學習 iOS Day19 - async/await 與串接 API

  • 分享至 

  • xImage
  •  

昨天我們聊到第三方框架的導入,今天要回來看 Swift 本身的一個特性 —— async/await,並把它跟 網路請求 (Alamofire)資料解析 (Codable) 串在一起,最後還會做一個小範例,把資料顯示在 SwiftUI 畫面上。這就是 App 串 API 的最小骨架。


為什麼需要 async/await?

在 iOS 開發裡,像是 API 請求下載圖片檔案存取,這些動作都需要時間,如果同步執行的話,App 就會「卡住」。所以我們必須用 非同步程式設計 來處理。

過去的 callback 方式很容易變成「callback hell」,讓程式不好維護,而 Swift 5.5 引入的 async/await 則讓我們可以用接近同步的語法,處理非同步邏輯。


URLSession 與 async/await

URLSession 是 iOS 內建的網路工具,iOS 15 之後,官方已經支援 async/await

基本使用方式:

let (data, response) = try await URLSession.shared.data(from: url)

這樣就能直接拿到伺服器回傳的 data。搭配 JSONDecoder,就能轉成我們要的型別。


什麼是 Codable?

在 Swift 裡,Codable 是一個協定(protocol),其實是由 EncodableDecodable 組合而成的:

  • Encodable:能把 Swift 型別轉成外部格式(例如 JSON)。
  • Decodable:能把外部格式(例如 JSON)轉成 Swift 型別。

也就是說,Codable 可以幫我們在「資料 ↔ Swift 物件」之間互轉

假設 API 回傳的 JSON 如下:

{
  "id": 1,
  "title": "Hello Swift",
  "body": "這是一篇測試文章"
}

我們只要定義一個 Codable 結構,就能輕鬆解析:

struct Post: Codable, Identifiable {
    let id: Int
    let title: String
    let body: String
}

然後透過 JSONDecoder

let post = try JSONDecoder().decode(Post.self, from: data)

反過來,如果我們要把 Post 轉回 JSON:

let post = Post(id: 1, title: "Hello Swift", body: "這是一篇測試文章")
let jsonData = try JSONEncoder().encode(post)

實戰小專案:抓文章清單並顯示在 List

我們使用 JSONPlaceholder 的測試 API:

https://jsonplaceholder.typicode.com/posts

Step 1: 定義 Model

struct Post: Codable, Identifiable {
    let id: Int
    let title: String
    let body: String
}

Step 2: 使用 Alamofire 發 API

這裡用 Alamofire 5.10.2,它已經支援 async/awaitserializingDecodable 方法。

import Alamofire

class PostService {
    func fetchPosts() async throws -> [Post] {
        let url = "https://jsonplaceholder.typicode.com/posts"
        return try await AF.request(url)
            .serializingDecodable([Post].self)
            .value
    }
}

如果是URLSession的寫法

class PostService {
    func fetchPosts() async throws -> [Post] {
        let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
        let (data, _) = try await URLSession.shared.data(from: url)
        let posts = try JSONDecoder().decode([Post].self, from: data)
        return posts
    }
}

這樣的好處是:

  1. 不需要自己解 data,Alamofire 直接幫我們解碼成 [Post]
  2. 網路錯誤會直接丟出 exception,不需要額外處理 (data, response)

Step 3: 建立 ViewModel

@MainActor
class PostViewModel: ObservableObject {
    @Published var posts: [Post] = []
    private let service = PostService()

    func loadPosts() async {
        do {
            posts = try await service.fetchPosts()
        } catch {
            print("載入失敗:\(error)")
        }
    }
}

Step 4: SwiftUI 介面

struct ContentView: View {
    @StateObject private var viewModel = PostViewModel()

    var body: some View {
        NavigationView {
            List(viewModel.posts) { post in
                VStack(alignment: .leading, spacing: 8) {
                    Text(post.title)
                        .font(.headline)
                    Text(post.body)
                        .font(.subheadline)
                        .foregroundColor(.gray)
                }
                .padding(.vertical, 4)
            }
            .navigationTitle("文章列表")
            .task {
                await viewModel.loadPosts()
            }
        }
    }
}

  • .task 是 SwiftUI 的一個修飾器,可以在畫面出現時執行非同步任務。

https://github.com/jian-fu-hung/ithelp-2025/blob/main/image/Day19/%E6%88%AA%E5%9C%96%202025-10-04%20%E6%99%9A%E4%B8%8A9.49.50.png?raw=true

今日小結

今天我們學到:

  1. async/await 在 Alamofire 中也能直接使用,透過 serializingDecodable 就能拿到型別化的資料。
  2. Alamofire 幫我們簡化了 URLSession 的細節,減少樣板程式。
  3. 串接 API → 解析 JSON → SwiftUI 顯示,整體程式碼更加乾淨。

上一篇
從零開始學習 iOS Day18 - 第三方套件
下一篇
從零開始學習 iOS Day20 - Swift 資料儲存基礎
系列文
從零開始學習 iOS24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言